#pragma once
#ifndef Utils_h__
#define Utils_h__

#include <eastl/vector.h>
#include <eastl/string.h>
#include <cstring>
#include <eastl/map.h>
#include <cassert>

#ifndef _MSC_VER
#undef __STRICT_ANSI__
#include <strings.h>
#define istrcmp strcmpi
#else
#define istrcmp _stricmp
#endif

#include "Geo.h"

///************************************************************************/
/// Easing
///************************************************************************/

enum eEasing {
	EASING_NONE,
	EASING_IN,
	EASING_OUT,
	EASING_INOUT, 
	EASING_OUTIN,
	EASING_BACKIN,
	EASING_OUTBACK,
	EASING_OUTELASTIC,
	EASING_INELASTIC,
	EASING_INBOUNCE,
	EASING_OUTBOUNCE,
	EASING_INOUTBOUNCE
};

float Ease(eEasing aType, float t, float max_t, float start, float offset);

///************************************************************************/
/// Noise
///************************************************************************/

float Noise(float x, float y);

///************************************************************************/
/// Curves
///************************************************************************/

Vec BezierQuadratic(float t, const Vec& p0, const Vec& p1, const Vec& p2);
Vec BezierCubic(float t, const Vec& p0, const Vec& p1, const Vec& p2, const Vec& p3);
Vec CatmulRom(float t, const Vec& P0, const Vec& P1, const Vec& P2, const Vec& P3);

template <typename T> inline
T Linear(float t, const T& v1, const T& v2) { return v1 + (v2-v1)*t; }

///************************************************************************/
/// DisplayVar
///************************************************************************/

template <typename T>
struct DisplayVar
{
	T Value;
	double DisplayValue;
	float UpdateSpeed;

	DisplayVar(float updatespeed = 3.0f) : UpdateSpeed(updatespeed) {}
	void Update(float dt) {
		DisplayValue += (double(Value) - DisplayValue) * dt * UpdateSpeed;
	}
	T Display() const {
		return (T)(DisplayValue+0.5);
	}
	void Set(T val) {
		Value = val;
		DisplayValue = double(val);
	}
};

///************************************************************************/
/// Pool
///************************************************************************/

template <typename T>
struct Pool
{
	eastl::vector<T> objs;
	eastl::vector<T*> ptrs;

	Pool(size_t start) : objs(start), ptrs(start)
	{
		objs.resize(start);
		ptrs.reserve(start);
		for (size_t i=0; i<start; i++)
		{
			objs[i].alive = false;
			ptrs.push_back(&objs[i]);
		}
	}

	T* Get()
	{
		if (ptrs.size())
		{
			T* obj = ptrs.back();
			obj->alive = true;
			ptrs.pop_back();
			return obj;
		}
		return 0;
	}

	void Unget(T* obj)
	{
		obj->alive = false;
		ptrs.push_back(obj);
	}
};

///************************************************************************/
/// Grid
///************************************************************************/

template <typename T>
struct Grid
{
	eastl::vector<T> elements;
	size_t width, height;

	Grid(size_t x, size_t y) : width(x), height(y), elements(x*y)
	{

	}

	Grid() : width(0), height(0) {}

	void Resize(size_t w, size_t h)
	{
		width = w;
		height = h;
		elements.resize(w*h);
	}

	T& operator ()(size_t x, size_t y)
	{
		assert(IsValid((int)x,(int)y));
		return elements[x + y * width];
	}

	const T &operator ()(size_t x, size_t y) const
	{
		assert(IsValid(x,y));
		return elements[x + y * width];
	}

	T& operator ()(Vec pos)
	{
		assert(IsValid((int)pos.X,(int)pos.Y));
		return elements[pos.X + pos.Y * width];
	}

	const T &operator ()(Vec pos) const
	{
		assert(IsValid((int)pos.X,(int)pos.Y));
		return elements[pos.X + pos.Y * width];
	}

	bool IsValid(int x, int y) const { return x >= 0 && y >= 0 && x < (int)width && y < (int)height; }

	template <typename FUNC>
	void MapMethodPointer(FUNC func)
	{
		for (size_t i=0; i<elements.size(); i++)
			((elements[i]).*(func))(i%width, i/width);
	}

	template <typename FUNC>
	void Map(FUNC func)
	{
		for (size_t i=0; i<elements.size(); i++)
			func(i%width, i/width);
	}

	template <typename FUNC>
	void MapElements(FUNC func)
	{
		for (size_t i=0; i<elements.size(); i++)
			func(elements[i]);
	}

};

///************************************************************************/
/// Movement Costs
///************************************************************************/

struct MCTileInfo
{
	int TileType;
	int avMP;
	int PX, PY;
};

struct IMovementCost
{
	void Calculate(int startx, int starty, int mp, void* data);

	virtual bool ValidPos(int x, int y) = 0;
	virtual int GetWalkCost(void* data, int tiletype) = 0;
	virtual MCTileInfo& GetTile(int x, int y) = 0;
	virtual void SetTile(int x, int y, const MCTileInfo& info) = 0;

private:
	int mStartX;
	int mStartY;

	void CalculateMovementCosts(int x, int y, int MP, int px, int py, void* data);
};

struct MovementCost : IMovementCost
{
	Grid<MCTileInfo> Tiles;

	virtual bool ValidPos(int x, int y) { return Tiles.IsValid(x,y); }
	virtual MCTileInfo& GetTile(size_t x, size_t y) { return Tiles(x,y); }
	virtual void SetTile(size_t x, size_t y, const MCTileInfo& info) { Tiles(x,y) = info; }
};

///************************************************************************/
/// A*
///************************************************************************/

/// TODO
void SearchAStar(int);

///************************************************************************/
/// Maze
///************************************************************************/

enum Dirs
{
	UP,
	RIGHT,
	DOWN,
	LEFT
};

extern const int OfsX[4];
extern const int OfsY[4];
extern const int RevOfsX[4];
extern const int RevOfsY[4];
extern const int RevDir[4];

struct MazeTile
{
	int Exits;
	int DistanceToStart;
	bool Visited;

	MazeTile() : Exits(0), DistanceToStart(-1), Visited(false) {}
	void Reset(size_t x, size_t y) { Exits = 0; DistanceToStart = -1; Visited = false; }
};

typedef Grid<MazeTile> CoolMaze;

void GenerateMaze(CoolMaze& maze, int start_x, int start_y);

///************************************************************************/
/// Tilemap
///************************************************************************/

/// TODO
struct Tilemap
{
	typedef eastl::map<eastl::string,eastl::string> Props;

	struct Layer
	{
		eastl::string Name;
		Props Properties;
	};

	struct Object
	{
		enum ObjectType
		{
			OT_POINT,
			OT_RECTANGLE,
			OT_POLYGON,
			OT_POLYLINE,
			OT_TILE
		};

		ObjectType Type;

		Vec Position;
		Vec Size;

		eastl::string Name;
		eastl::string TypeName;

		uint TileID;

		eastl::vector<Vec> Points;

		Props Properties;
	};

	struct TileLayer : Layer
	{
		Grid<size_t> Tiles;
	};

	struct ObjLayer : Layer
	{
		eastl::vector<Object> Objects;
	};

	struct TileDef
	{
		Props Properties;
	};

	struct Tileset
	{
		size_t FirstGID;
		eastl::vector<TileDef> TileDefs;
		eastl::string Name;
		eastl::string Image;
		size_t TileWidth, TileHeight;
		Color TransparentColor;
	};

	size_t TileWidth, TileHeight;
	size_t Width, Height;

	eastl::vector<Tileset*> Tilesets;
	eastl::vector<Layer*> Layers;

	Props Properties;
};

///************************************************************************/
/// IO
///************************************************************************/

eastl::string LoadFile(const char* filename);

///************************************************************************/
/// Strings
///************************************************************************/

void Tokenize(const char* input, eastl::vector<eastl::string>& tokens, const char* delims = " \r\n\t");

///************************************************************************/
/// Misc
///************************************************************************/

float RandFloat(float low = 0.0f, float high = 1.0f);

inline bool Eq(const char* one, const char* two) { return !strcmp(one,two); }
//inline bool EqI(const char* one, const char* two) { return !istrcmp(one,two); }

/// Finds and erases an element in a container
template <typename CT, typename CVT>
bool FindAndErase(CT& cont, const CVT& value); /// by value
template <typename CT, typename CKT>
bool FindAndEraseMap(CT& cont, const CKT& key); /// by key in map

/// Searches for an element in a container and returns it or `not_found_val` if not found
template <typename CT, typename CVT>
CVT FindOr(CT& cont, const CVT& value, const CVT& not_found_val);
template <typename CT, typename CVT>
const CVT& FindOrConst(CT& cont, const CVT& value, const CVT& not_found_val);

/// Searches for an element in a map by key and returns it or `not_found_val` if not found
template <typename CT, typename CKT, typename CVT>
CVT FindInMap(CT& cont, const CKT& key, const CVT& not_found_val);
template <typename CT, typename CKT, typename CVT>
const CVT& FindInMapConst(CT& cont, const CKT& key, const CVT& not_found_val);

template <typename CT, typename PRED>
void EraseIf(CT& cont, PRED predicate);

///************************************************************************/
/// IMPLEMENTATIONS
///************************************************************************/

template <typename CT, typename CVT>
bool FindAndErase(CT& cont, const CVT& value)
{
	typename CT::iterator it = eastl::find(cont.begin(), cont.end(), value);
	if (it != cont.end())
	{
		cont.erase(it);
		return true;
	}
	return false;
}

template <typename CT, typename CKT>
bool FindAndEraseMap(CT& cont, const CKT& key)
{
	typename CT::iterator it = cont.find(key);
	if (it != cont.end())
	{
		cont.erase(it);
		return true;
	}
	return false;
}

template <typename CT, typename CKT, typename CVT>
CVT FindInMap(CT& cont, const CKT& key, const CVT& not_found_val)
{
	typename CT::iterator it = cont.find(key);
	if (it != cont.end())
		return it->second;
	return not_found_val;
}

template <typename CT, typename CKT, typename CVT>
const CVT& FindInMapConst(CT& cont, const CKT& key, const CVT& not_found_val)
{
	typename CT::const_iterator it = cont.find(key);
	if (it != cont.end())
		return it->second;
	return not_found_val;
}

template <typename CT, typename CVT>
CVT FindOr(CT& cont, const CVT& value, const CVT& not_found_val)
{
	typename CT::iterator it = eastl::find(cont.begin(), cont.end(), value);
	if (it != cont.end())
	{
		return *it;
	}
	return not_found_val;
}

template <typename CT, typename CVT>
const CVT& FindOrConst(CT& cont, const CVT& value, const CVT& not_found_val)
{
	typename CT::const_iterator it = eastl::find(cont.begin(), cont.end(), value);
	if (it != cont.end())
	{
		return *it;
	}
	return not_found_val;
}


template <typename CT, typename PRED>
void EraseIf(CT& cont, PRED predicate)
{
	cont.erase(eastl::remove_if(cont.begin(), cont.end(), predicate), cont.end());
}

#endif // Utils_h__
